---
title: useEffect Hook
description: "Details about the useEffect React hook in ReScript"
canonical: "/docs/react/hooks-effect"
section: "Hooks & State Management"
order: 2
---

# useEffect

<Intro>

The _Effect_ Hook lets you perform side effects in function components.

</Intro>

## What are Effects?

Common examples for (side) effects are data fetching, setting up a subscription, and manually changing the DOM in React components.

There are two common kinds of side effects in React components: those that don’t require cleanup, and those that do. We'll look into the distinction later on in our examples, but first let's see how the interface looks like.

## Basic Usage

<CodeTab labels={["ReScript", "JS Output"]}>

```res example
// Runs after every completed render
React.useEffectOnEveryRender(() => {
  // Run effects
  None // or Some(() => {})
})


// Runs only once right after mounting the component
React.useEffect(() => {
  // Run effects
  None // or Some(() => {})
}, [])

// Dummy props
let prop1 = 1 and prop2 = 2 and prop3 = 3

// Runs everytime `prop1` has changed
React.useEffect(() => {
  // Run effects based on prop1
  None
}, [prop1])

// Runs everytime `prop1` or `prop2` has changed
React.useEffect(() => {
  // Run effects based on prop1 / prop2
  None
}, (prop1, prop2))

React.useEffect(() => {
  None
}, (prop1, prop2, prop3))
```

```js
React.useEffect(function () {});
React.useEffect(function () {}, []);
React.useEffect(function () {}, [prop1]);
React.useEffect(function () {}, [prop1, prop2]);
React.useEffect(function () {}, [prop1, prop2, prop3]);
```

</CodeTab>

`React.useEffect` receives a function that contains imperative, possibly effectful code, and returns a value `option<unit => unit>` as a potential cleanup function.

A `useEffect` call may receive an additional array of dependencies. The effect function will run whenever one of the provided dependencies has changed. More details on why this is useful [down below](#effect-dependencies).

**Note:** You probably wonder why the `React.useEffect` with only one dependency receives an `array`, while `useEffect`'s with multiple dependencies require a `tuple` (e.g. `(prop1, prop2)`) for the dependency list. That's because a tuple can receive multiple values of different types, whereas an `array` only accepts values of identical types. It's possible to replicate a `useEffect` with multiple dependencies by doing `React.useEffect(fn, [1, 2])`, on other hand the type checker wouldn't allow `React.useEffect(fn, [1, "two"])`.

`React.useEffectOnEveryRender` will run its function after every completed render, while `React.useEffect` will only run the effect on the first render (when the component has mounted).

## Examples

### Effects without Cleanup

Sometimes, we want to run some additional code after React has updated the DOM. Network requests, manual DOM mutations, and logging are common examples of effects that don’t require a cleanup. We say that because we can run them and immediately forget about them.

As an example, let's write a counter component that updates `document.title` on every render:

```res prelude
// Counter.res
module Document = {
  type t
  @val external document: t = "document"
  @set external setTitle: (t, string) => unit = "title"
}
```

<CodeTab labels={["ReScript", "JS Output"]}>

```res example

@react.component
let make = () => {
  let (count, setCount) = React.useState(_ => 0)

  React.useEffectOnEveryRender(() => {
    open! Document
    document->setTitle(`You clicked ${Belt.Int.toString(count)} times!`)
    None
  })

  let onClick = (_evt) => {
    setCount(prev => prev + 1)
  }

  let msg = "You clicked" ++ Belt.Int.toString(count) ++  "times"

  <div>
    <p>{React.string(msg)}</p>
    <button onClick> {React.string("Click me")} </button>
  </div>
}
```

```js
function Counter(Props) {
  var match = React.useState(function () {
    return 0;
  });
  var setCount = match[1];
  var count = match[0];
  React.useEffect(function () {
    document.title = "You clicked " + String(count) + " times!";
  });
  var onClick = function (_evt) {
    return Curry._1(setCount, function (prev) {
      return (prev + 1) | 0;
    });
  };
  var msg = "You clicked" + String(count) + "times";
  return React.createElement(
    "div",
    undefined,
    React.createElement("p", undefined, msg),
    React.createElement(
      "button",
      {
        onClick: onClick,
      },
      "Click me",
    ),
  );
}
```

</CodeTab>

In case we want to make the effects dependent on `count`, we can just use following `useEffect` call instead:

```res
 React.useEffect(() => {
    open! Document
    document->setTitle(`You clicked ${Belt.Int.toString(count)} times!`)
    None
  }, [count])
```

Now instead of running an effect on every render, it will only run when `count` has a different value than in the render before.

### Effects with Cleanup

Earlier, we looked at how to express side effects that don’t require any cleanup. However, some effects do. For example, we might want to set up a subscription to some external data source. In that case, it is important to clean up so that we don’t introduce a memory leak!

Let's look at an example that gracefully subscribes, and later on unsubscribes from some subscription API:

```res prelude
module ChatAPI = {
  // Imaginary globally available ChatAPI for demo purposes
  type status = { isOnline: bool }
  @val external subscribeToFriendStatus: (string, status => unit) => unit = "subscribeToFriendStatus"
  @val external unsubscribeFromFriendStatus: (string, status => unit) => unit = "unsubscribeFromFriendStatus"
}

type state = Offline | Loading | Online
```

<CodeTab labels={["ReScript", "JS Output"]}>

{/* TODO: fix this example */}

```res
// FriendStatus.res
@react.component
let make = (~friendId: string) => {
  let (state, setState) = React.useState(_ => Offline)

  React.useEffectOnEveryRender(() => {
    let handleStatusChange = (status) => {
      setState(_ => {
        status.ChatAPI.isOnline ? Online : Offline
      })
    }

    ChatAPI.subscribeToFriendStatus(friendId, handleStatusChange)
    setState(_ => Loading)

    let cleanup = () => {
      ChatAPI.unsubscribeFromFriendStatus(friendId, handleStatusChange)
    }

    Some(cleanup)
  })

  let text = switch(state) {
    | Offline => friendId ++ " is offline"
    | Online => friendId ++ " is online"
    | Loading => "loading..."
  }

  <div>
  	{React.string(text)}
  </div>
}
```

```js
function FriendStatus(Props) {
  var friendId = Props.friendId;
  var match = React.useState(function () {
    return /* Offline */ 0;
  });
  var setState = match[1];
  React.useEffect(function () {
    var handleStatusChange = function (status) {
      return Curry._1(setState, function (param) {
        if (status.isOnline) {
          return /* Online */ 2;
        } else {
          return /* Offline */ 0;
        }
      });
    };
    subscribeToFriendStatus(friendId, handleStatusChange);
    Curry._1(setState, function (param) {
      return /* Loading */ 1;
    });
    return function (param) {
      unsubscribeFromFriendStatus(friendId, handleStatusChange);
    };
  });
  var text;
  switch (match[0]) {
    case /* Offline */ 0:
      text = friendId + " is offline";
      break;
    case /* Loading */ 1:
      text = "loading...";
      break;
    case /* Online */ 2:
      text = friendId + " is online";
      break;
  }
  return React.createElement("div", undefined, text);
}
```

</CodeTab>

## Effect Dependencies

In some cases, cleaning up or applying the effect after every render might create a performance problem. Let's look at a concrete example to see what `useEffect` does:

```res
// from a previous example above
React.useEffect(() => {
  open! Document
  document->setTitle(`You clicked ${Belt.Int.toString(count)} times!`)
  None
}, [count])
```

Here, we pass `[count]` to `useEffect` as a dependency. What does this mean? If the `count` is 5, and then our component re-renders with count still equal to 5, React will compare `[5]` from the previous render and `[5]` from the next render. Because all items within the array are the same (5 === 5), React would skip the effect. That’s our optimization.

When we render with count updated to 6, React will compare the items in the `[5]` array from the previous render to items in the `[6]` array from the next render. This time, React will re-apply the effect because `5 !== 6`. If there are multiple items in the array, React will re-run the effect even if just one of them is different.

This also works for effects that have a cleanup phase:

{/* TODO fix this example */}

```res
@react.component
let make = (~friendId: string) => {
  let (state, setState) = React.useState(_ => Offline)
  // from a previous example above
  React.useEffect(() => {
    let handleStatusChange = (status) => {
      setState(_ => {
        status.ChatAPI.isOnline ? Online : Offline
      })
    }

    ChatAPI.subscribeToFriendStatus(friendId, handleStatusChange)
    setState(_ => Loading)

    let cleanup = () => {
      ChatAPI.unsubscribeFromFriendStatus(friendId, handleStatusChange)
    }

    Some(cleanup)
  }, [friendId]) // Only re-subscribe if friendId changes
}
```

**Important:** If you use this optimization, make sure the array includes all values from the component scope (such as props and state) that change over time and that are used by the effect. Otherwise, your code will reference stale values from previous renders

If you want to run an effect and clean it up only once (on mount and unmount), use `React.useEffect` with an empty dependency array `[]`.

If you are interested in learning about performance optimization related topics, have a look at the ReactJS [improving application performance](https://react.dev/learn/build-a-react-app-from-scratch#improving-application-performance) for more detailed infos.
